// ParticleSystem.cpp: implementation of the CParticleSystem class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "ParticleSystem.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CBaseParticleEmiter::CBaseParticleEmiter( DWORD dwCount, DWORD dwVar, FLOAT fVel, FLOAT fVarVel, FLOAT fLife, FLOAT fVarLife, FLOAT fSize, FLOAT fVarSize, DWORD dwType ) : m_dwAvgEmitCount( dwCount ),
																																											m_dwVarEmitCount( dwVar ),
																																											m_fAvgVelocity( fVel ),
																																											m_fVarVelocity( fVarVel ),
																																											m_fAvgLifeTime( fLife ),
																																											m_fVarLifeTime( fVarLife ),
																																											m_fAvgSize( fSize ),
																																											m_fVarSize( fVarSize ),
																																											m_dwType( dwType ),
																																											m_fEmitInterval( 0.25f ),
																																											m_fLastEmitTime( 0.0f ),
																																											m_ppsSystem( NULL )
{
}

CBaseParticleEmiter::CBaseParticleEmiter() : m_dwAvgEmitCount( 10 ),
											 m_dwVarEmitCount( 0 ),
											 m_fAvgVelocity( 10.0f ),
											 m_fVarVelocity( 2.0f ),
											 m_fAvgLifeTime( 5.0f ),
											 m_fVarLifeTime( 0.0f ),
											 m_fAvgSize( 1.0f ),
											 m_fVarSize( 0.0f ),
											 m_dwType( PT_BLURED|PT_EMITSSPARKS ),
											 m_fEmitInterval( 0.5f ),
											 m_fLastEmitTime( 0.0f ),
											 m_ppsSystem( NULL )
{
}

CBaseParticleEmiter::~CBaseParticleEmiter()
{
}

VOID CBaseParticleEmiter::Attach( CParticleSystem* pSystem )
{
	m_ppsSystem = pSystem;
}

VOID CBaseParticleEmiter::Detach()
{
	m_ppsSystem = NULL;
}

VOID CBaseParticleEmiter::SetEmitCount( DWORD dwAvgEmitCount, DWORD dwVarEmitCount )
{
	m_dwAvgEmitCount = dwAvgEmitCount;
	m_dwVarEmitCount = dwVarEmitCount;
}

VOID CBaseParticleEmiter::GetEmitCount( DWORD& dwAvgEmitCount, DWORD& dwVarEmitCount )
{
	dwAvgEmitCount = m_dwAvgEmitCount;
	dwVarEmitCount = m_dwVarEmitCount;
}

VOID CBaseParticleEmiter::SetVelocity( FLOAT fAvgVelocity, FLOAT fVarVelocity )
{
	m_fAvgVelocity = fAvgVelocity;
	m_fVarVelocity = fVarVelocity;
}

VOID CBaseParticleEmiter::GetVelocity( FLOAT& fAvgVelocity, FLOAT& fVarVelocity )
{
	fAvgVelocity = m_fAvgVelocity;
	fVarVelocity = m_fVarVelocity;
}

VOID CBaseParticleEmiter::SetLifeTime( FLOAT fAvgLifeTime, FLOAT fVarLifeTime )
{
	m_fAvgLifeTime = fAvgLifeTime;
	m_fVarLifeTime = fVarLifeTime;
}

VOID CBaseParticleEmiter::GetLifeTime( FLOAT& fAvgLifeTime, FLOAT& fVarLifeTime )
{
	fAvgLifeTime = m_fAvgLifeTime;
	fVarLifeTime = m_fVarLifeTime;
}

VOID CBaseParticleEmiter::SetSize( FLOAT fAvgSize, FLOAT fVarSize )
{
	m_fAvgSize = fAvgSize;
	m_fVarSize = fVarSize;
}

VOID CBaseParticleEmiter::GetSize( FLOAT& fAvgSize, FLOAT& fVarSize )
{
	fAvgSize = m_fAvgSize;
	fVarSize = m_fVarSize;
}

VOID CBaseParticleEmiter::SetParticleType( DWORD dwType )
{
	m_dwType = dwType;
}

DWORD CBaseParticleEmiter::GetParticleType()
{
	return m_dwType;
}

VOID CBaseParticleEmiter::SetEmitInterval( FLOAT fTime )
{
	m_fEmitInterval = fTime;
}

FLOAT CBaseParticleEmiter::GetEmitInterval()
{
	return m_fEmitInterval;
}

CPointEmiter::CPointEmiter( DWORD dwCount, DWORD dwVar, FLOAT fVel, FLOAT fVarVel, FLOAT fLife, FLOAT fVarLife, FLOAT fSize, FLOAT fVarSize, DWORD dwType, he3d_CVector& vPos, he3d_CVector& vDir, FLOAT fAngle ) : CBaseParticleEmiter( dwCount, dwVar, fVel, fVarVel, fLife, fVarLife, fSize, fVarSize, dwType ),
																																																					m_vEmiterPos( vPos ),																																																					
																																																					m_fEmitAngle( fAngle )
{
	m_vEmiterDir = Normalize( vDir );
}

CPointEmiter::CPointEmiter() : CBaseParticleEmiter(),
							   m_vEmiterPos( 0.0f, 0.0f, 0.0f ), 
							   m_vEmiterDir( 0.0f, 1.0f, 0.0f ),
							   m_fEmitAngle( H_2PI )
{
}

CPointEmiter::~CPointEmiter()
{
}

VOID CPointEmiter::Emit( FLOAT fTime )
{
	if( fTime - m_fLastEmitTime < m_fEmitInterval )
		return;

	m_fLastEmitTime = fTime;

	if( !m_ppsSystem )
		return;

	if( m_ppsSystem->m_dwParticleCount >= m_ppsSystem->m_dwMaxParticles )
		return;

	DWORD					dwEmit = m_dwAvgEmitCount + m_dwVarEmitCount - rand()%2*m_dwVarEmitCount;
	PPARTICLE				pParticle;
	he3d_CVector			vVel;	

	if( dwEmit > m_ppsSystem->m_dwMaxParticles - m_ppsSystem->m_dwParticleCount )
		dwEmit = m_ppsSystem->m_dwMaxParticles - m_ppsSystem->m_dwParticleCount;

	m_ppsSystem->m_dwParticleCount += dwEmit;

	for( DWORD i = 0 ; i < dwEmit ; i++ )
	{
		if( m_ppsSystem->m_pParticleFree )
		{		
			pParticle = m_ppsSystem->m_pParticleFree;
			m_ppsSystem->m_pParticleFree = m_ppsSystem->m_pParticleFree->next;
		}
		else
		{
			pParticle = new PARTICLE;
		}

		pParticle->next = m_ppsSystem->m_pParticle;
		m_ppsSystem->m_pParticle = pParticle;

		vVel = ( m_fAvgVelocity + SIGNEDRAND()*m_fVarVelocity )*m_vEmiterDir;
		
		pParticle->vV0 = vVel*RotationMtx( m_fEmitAngle*SIGNEDRAND(), m_fEmitAngle*SIGNEDRAND(), m_fEmitAngle*SIGNEDRAND() );
		pParticle->vP0 = m_vEmiterPos;
		pParticle->vP = m_vEmiterPos;
		pParticle->fBornTime = fTime;
		pParticle->fLifeTime = m_fAvgLifeTime + SIGNEDRAND()*m_fVarLifeTime;
		pParticle->fSize = m_fAvgSize + SIGNEDRAND()*m_fVarSize;				
		pParticle->dwType = m_dwType;
	}
}

VOID CPointEmiter::SetEmiterPos( he3d_CVector& v )
{
	m_vEmiterPos = v;
}

he3d_CVector CPointEmiter::GetEmiterPos()
{
	return m_vEmiterPos;
}

VOID CPointEmiter::SetEmiterDir( he3d_CVector& v )
{
	m_vEmiterDir = Normalize( v );
}

he3d_CVector CPointEmiter::GetEmiterDir()
{
	return m_vEmiterDir;
}

VOID CPointEmiter::SetEmitAngle( FLOAT fAngle )
{
	m_fEmitAngle = fAngle;
}

FLOAT CPointEmiter::GetEmitAngle()
{
	return m_fEmitAngle;
}

CFinitePointEmiter::CFinitePointEmiter( DWORD dwCount, DWORD dwVar, FLOAT fVel, FLOAT fVarVel, FLOAT fLife, FLOAT fVarLife, FLOAT fSize, FLOAT fVarSize, DWORD dwType, DWORD dwDim, he3d_CVector& vPos, he3d_CVector& vDir, FLOAT fAngle ) : CPointEmiter( dwCount, dwVar, fVel, fVarVel, fLife, fVarLife, fSize, fVarSize, dwType, vPos, vDir, fAngle ),
																																																											 m_dwDimension( dwDim ),
																																																											 m_dwEmitedCount( 0 ),
																																																											 m_bAutoDelete( TRUE )
{
}

CFinitePointEmiter::~CFinitePointEmiter()
{
}

CFinitePointEmiter::CFinitePointEmiter() : CPointEmiter(),
										   m_dwDimension( 0xffffffff ),
										   m_dwEmitedCount( 0 ),
										   m_bAutoDelete( TRUE )
{
}

VOID CFinitePointEmiter::Emit( FLOAT fTime )
{
	if( fTime - m_fLastEmitTime < m_fEmitInterval )
		return;

	m_fLastEmitTime = fTime;

	if( !m_ppsSystem )
		return;

	if( m_ppsSystem->m_dwParticleCount >= m_ppsSystem->m_dwMaxParticles )
		return;

	DWORD					dwEmit = m_dwAvgEmitCount + m_dwVarEmitCount - rand()%2*m_dwVarEmitCount;
	PPARTICLE				pParticle;
	he3d_CVector			vVel;	

	if( dwEmit > m_ppsSystem->m_dwMaxParticles - m_ppsSystem->m_dwParticleCount )
		dwEmit = m_ppsSystem->m_dwMaxParticles - m_ppsSystem->m_dwParticleCount;

	if( dwEmit >= m_dwDimension - m_dwEmitedCount )
		dwEmit = m_dwDimension - m_dwEmitedCount;

	m_dwEmitedCount += dwEmit;
	m_ppsSystem->m_dwParticleCount += dwEmit;

	for( DWORD i = 0 ; i < dwEmit ; i++ )
	{
		if( m_ppsSystem->m_pParticleFree )
		{		
			pParticle = m_ppsSystem->m_pParticleFree;
			m_ppsSystem->m_pParticleFree = m_ppsSystem->m_pParticleFree->next;
		}
		else
		{
			pParticle = new PARTICLE;
		}

		pParticle->next = m_ppsSystem->m_pParticle;
		m_ppsSystem->m_pParticle = pParticle;

		vVel = ( m_fAvgVelocity + SIGNEDRAND()*m_fVarVelocity )*m_vEmiterDir;
		
		pParticle->vV0 = vVel*RotationMtx( m_fEmitAngle*SIGNEDRAND(), m_fEmitAngle*SIGNEDRAND(), m_fEmitAngle*SIGNEDRAND() );
		pParticle->vP0 = m_vEmiterPos;
		pParticle->vP = m_vEmiterPos;
		pParticle->fBornTime = fTime;
		pParticle->fLifeTime = m_fAvgLifeTime + SIGNEDRAND()*m_fVarLifeTime;
		pParticle->fSize = m_fAvgSize + SIGNEDRAND()*m_fVarSize;				
		pParticle->dwType = m_dwType;
	}	
}

VOID CFinitePointEmiter::EnableAutoDelete( BOOL bEnable )
{
	m_bAutoDelete = FALSE;
}

VOID CFinitePointEmiter::SetDimension( DWORD dwDim )
{
	m_dwDimension = dwDim;
}

DWORD CFinitePointEmiter::GetDimension()
{
	return m_dwDimension;
}

DWORD CFinitePointEmiter::GetEmitedCount()
{
	return m_dwEmitedCount;
}

CSphericalEmiter::CSphericalEmiter( DWORD dwCount, DWORD dwVar, FLOAT fVel, FLOAT fVarVel, FLOAT fLife, FLOAT fVarLife, FLOAT fSize, FLOAT fVarSize, DWORD dwType, he3d_CVector& vCenter, FLOAT fRadius ) : CBaseParticleEmiter( dwCount, dwVar, fVel, fVarVel, fLife, fVarLife, fSize, fVarSize, dwType ),
																																																			m_vCenter( vCenter ),
																																																			m_fRadius( fRadius )
{
}

CSphericalEmiter::CSphericalEmiter() : CBaseParticleEmiter(),
									   m_vCenter( 0.0f, 0.0f, 0.0f ),
									   m_fRadius( 10.0f )
{
}

CSphericalEmiter::~CSphericalEmiter()
{
}

VOID CSphericalEmiter::Emit( FLOAT fTime )
{
	if( fTime - m_fLastEmitTime < m_fEmitInterval )
		return;

	m_fLastEmitTime = fTime;

	if( !m_ppsSystem )
		return;	

	if( m_ppsSystem->m_dwParticleCount >= m_ppsSystem->m_dwMaxParticles )
		return;

	DWORD					dwEmit = m_dwAvgEmitCount + m_dwVarEmitCount - rand()%2*m_dwVarEmitCount;
	PPARTICLE				pParticle;
	he3d_CVector			vVel, vPos;	
	FLOAT					fAlpha, fBeta;

	if( dwEmit > m_ppsSystem->m_dwMaxParticles - m_ppsSystem->m_dwParticleCount )
		dwEmit = m_ppsSystem->m_dwMaxParticles - m_ppsSystem->m_dwParticleCount;	
	
	m_ppsSystem->m_dwParticleCount += dwEmit;

	for( DWORD i = 0 ; i < dwEmit ; i++ )
	{
		if( m_ppsSystem->m_pParticleFree )
		{		
			pParticle = m_ppsSystem->m_pParticleFree;
			m_ppsSystem->m_pParticleFree = m_ppsSystem->m_pParticleFree->next;
		}
		else
		{
			pParticle = new PARTICLE;
		}

		fAlpha = H_2PI*RAND();
		fBeta = H_2PI*RAND();

		vPos = he3d_CVector( m_fRadius*cosf(fAlpha)*sinf(fBeta), m_fRadius*cosf(fBeta), m_fRadius*sinf(fAlpha)*sinf(fBeta) ) + m_vCenter;
		vVel = Normalize( m_vCenter - vPos );

		pParticle->next = m_ppsSystem->m_pParticle;
		m_ppsSystem->m_pParticle = pParticle;		
		
		pParticle->vV0 = ( m_fAvgVelocity + SIGNEDRAND()*m_fVarVelocity )*vVel;
		pParticle->vP0 = vPos;
		pParticle->vP = vPos;
		pParticle->fBornTime = fTime;
		pParticle->fLifeTime = m_fAvgLifeTime + SIGNEDRAND()*m_fVarLifeTime;
		pParticle->fSize = m_fAvgSize + SIGNEDRAND()*m_fVarSize;				
		pParticle->dwType = m_dwType;
	}	
}

VOID CSphericalEmiter::SetCenter( he3d_CVector& vCenter )
{
	m_vCenter = vCenter;
}

he3d_CVector CSphericalEmiter::GetCenter()
{
	return m_vCenter;
}

VOID CSphericalEmiter::SetRadius( FLOAT fRadius )
{
	m_fRadius = fRadius;
}

FLOAT CSphericalEmiter::GetRadius()
{
	return m_fRadius;
}


CPlanarEmiter::CPlanarEmiter( DWORD dwCount, DWORD dwVar, FLOAT fVel, FLOAT fVarVel, FLOAT fLife, FLOAT fVarLife, FLOAT fSize, FLOAT fVarSize, DWORD dwType, he3d_CVector& vDir, FLOAT fPlaneSize, FLOAT fHeight ) : CBaseParticleEmiter( dwCount, dwVar, fVel, fVarVel, fLife, fVarLife, fSize, fVarSize, dwType ),
																																																					 m_vDir( vDir ),
																																																					 m_fSize( fPlaneSize ),
																																																					 m_fHeight( fHeight )
{
}

CPlanarEmiter::CPlanarEmiter() : CBaseParticleEmiter(),
								 m_vDir( 0.0f, -1.0f, 0.0f ),
								 m_fSize( 100.0f ),
								 m_fHeight( 100.0f )
{
}

CPlanarEmiter::~CPlanarEmiter()
{
}

VOID CPlanarEmiter::Emit( FLOAT fTime )
{
	if( fTime - m_fLastEmitTime < m_fEmitInterval )
		return;

	m_fLastEmitTime = fTime;

	if( !m_ppsSystem )
		return;

	if( m_ppsSystem->m_dwParticleCount >= m_ppsSystem->m_dwMaxParticles )
		return;

	DWORD					dwEmit = m_dwAvgEmitCount + m_dwVarEmitCount - rand()%2*m_dwVarEmitCount;
	PPARTICLE				pParticle;
	he3d_CVector			vVel;	

	if( dwEmit > m_ppsSystem->m_dwMaxParticles - m_ppsSystem->m_dwParticleCount )
		dwEmit = m_ppsSystem->m_dwMaxParticles - m_ppsSystem->m_dwParticleCount;

	m_ppsSystem->m_dwParticleCount += dwEmit;

	for( DWORD i = 0 ; i < dwEmit ; i++ )
	{
		if( m_ppsSystem->m_pParticleFree )
		{		
			pParticle = m_ppsSystem->m_pParticleFree;
			m_ppsSystem->m_pParticleFree = m_ppsSystem->m_pParticleFree->next;
		}
		else
		{
			pParticle = new PARTICLE;
		}

		pParticle->next = m_ppsSystem->m_pParticle;
		m_ppsSystem->m_pParticle = pParticle;

		vVel = ( m_fAvgVelocity + SIGNEDRAND()*m_fVarVelocity )*m_vDir;
		
		pParticle->vV0 = vVel;
		pParticle->vP0 = he3d_CVector( m_fSize*SIGNEDRAND(), m_fHeight, m_fSize*SIGNEDRAND() );
		pParticle->vP = pParticle->vP0;
		pParticle->fBornTime = fTime;
		pParticle->fLifeTime = m_fAvgLifeTime + SIGNEDRAND()*m_fVarLifeTime;
		pParticle->fSize = m_fAvgSize + SIGNEDRAND()*m_fVarSize;				
		pParticle->dwType = m_dwType;
	}	
}


VOID CPlanarEmiter::SetEmiterSize( FLOAT fSize )
{
	m_fSize = fSize;
}

FLOAT CPlanarEmiter::GetEmiterSize()
{
	return m_fSize;
}

VOID CPlanarEmiter::SetHeight( FLOAT fHeight )
{
	m_fHeight = fHeight;
}

FLOAT CPlanarEmiter::GetHeight()
{
	return m_fHeight;
}

VOID CPlanarEmiter::SetDir( he3d_CVector& vDir )
{
	m_vDir = vDir;
}

he3d_CVector CPlanarEmiter::GetDir()
{
	return m_vDir;
}

CParticleSystem::CParticleSystem( DWORD dwMaxParticles, DWORD dwBufferSize, DWORD dwLockSize ) : m_dwMaxParticles( dwMaxParticles ),
																							     m_dwBufferSize( dwBufferSize ),
																								 m_dwLockSize( dwLockSize ),
																								 m_dwParticleCount( 0 ),
																								 m_dwCurrentOffset( 0 ),
																								 m_fBlurCoeff( 0.333f ),
																								 m_dwGravityType( G_VECTOR ),
																								 m_fGravity( 9.81f ),
																								 m_vGravity( 0.0f, -1.0f, 0.0f ),
																								 m_dwCollision( C_OFF ),
																								 m_vN( 0.0f, 1.0f, 0.0f ),
																								 m_fD( 0.0f ),
																								 m_pParticle( NULL ),
																								 m_pParticleFree( NULL ),
																								 m_pRoot( NULL ),
																								 m_dwSparksEmitCount( 4 ),
																								 m_fSparksEmitAngle( 0.6f ),
																								 m_fSparksFalloff( 0.2f ),
																								 m_fSparksLifeTime( 2.0f ),
																								 m_fSparksSize( 1.0f ),
																								 m_ptextTexture( NULL ),
																								 m_dwRenderType( RT_ADD ),
																								 m_pvbParticle( NULL )
																														
{	
	if( 2*m_dwLockSize > m_dwBufferSize )
		m_dwBufferSize = 2*m_dwLockSize;
}

BOOL CParticleSystem::Initialize( PDIRECT3DDEVICE8 pDevice )
{
	return SUCCEEDED( pDevice->CreateVertexBuffer( m_dwBufferSize*sizeof(PARTICLEVERTEX), D3DUSAGE_DYNAMIC|D3DUSAGE_POINTS, FVF_PARTICLEVERTEX, D3DPOOL_DEFAULT, &m_pvbParticle ) );
}

CParticleSystem::~CParticleSystem()
{
	if( m_pvbParticle )
		m_pvbParticle->Release();

	while( m_pRoot )
	{
		CEmiterListNode*	pNode = m_pRoot->m_pNext;
		m_pRoot->m_pEmiter->Detach();
		delete m_pRoot;
		m_pRoot = pNode;
	}

	PPARTICLE				pTmp;

	while( m_pParticle )
	{	
		pTmp = m_pParticle->next;
		delete m_pParticle;
		m_pParticle = pTmp;
	}

	while( m_pParticleFree )
	{
		pTmp = m_pParticleFree->next;
		delete m_pParticleFree;
		m_pParticleFree = pTmp;
	}
}

VOID CParticleSystem::ConfigureGravity( DWORD dwType, he3d_CVector& vDir, FLOAT fG )
{
	m_dwGravityType = dwType;
	m_vGravity = vDir;
	m_fGravity = fG;
}

VOID CParticleSystem::ConfigureSparks( DWORD dwCount, FLOAT fFalloff, FLOAT fAngle, FLOAT fLifeTime, FLOAT fSize )
{
	m_dwSparksEmitCount = dwCount;
	m_fSparksFalloff = fFalloff;
	m_fSparksEmitAngle = fAngle;
	m_fSparksLifeTime = fLifeTime;
	m_fSparksSize = fSize;
}

VOID CParticleSystem::SetCollision( DWORD dwCollision )
{
	m_dwCollision = dwCollision;
}

DWORD CParticleSystem::GetCollison()
{
	return m_dwCollision;
}

VOID CParticleSystem::SetPlane( he3d_CVector& n, FLOAT d )
{
	m_vN = n;
	m_fD = d;
}

VOID CParticleSystem::AddEmiter( CBaseParticleEmiter* pEmiter )
{
	CEmiterListNode*		pNode = new CEmiterListNode;

	pNode->m_pEmiter = pEmiter;
	pNode->m_pEmiter->Attach( this );
	pNode->m_pNext = m_pRoot;
	m_pRoot = pNode;
}

VOID CParticleSystem::RemoveEmiter( CBaseParticleEmiter* pEmiter )
{	
	CEmiterListNode*		pNode = m_pRoot;
	CEmiterListNode*		pTmp;

	if( m_pRoot->m_pEmiter == pEmiter )
	{
		m_pRoot = m_pRoot->m_pNext;
		delete pNode;
	}
	else
	{	
		while( pNode->m_pNext )
		{			
			if( pNode->m_pNext->m_pEmiter == pEmiter )
			{					
				pTmp = pNode->m_pNext;
				pNode->m_pNext = pNode->m_pNext->m_pNext;
				delete pTmp;
				break;
			}

			pNode = pNode->m_pNext;
		}
	}
}

VOID CParticleSystem::SetTexture( PDIRECT3DTEXTURE8 ptext )
{
	m_ptextTexture = ptext;
}

PDIRECT3DTEXTURE8 CParticleSystem::GetTexture()
{
	return m_ptextTexture;
}

VOID CParticleSystem::SetBlurCoeff( FLOAT fCoeff )
{
	m_fBlurCoeff = fCoeff;
}

FLOAT CParticleSystem::GetBlurCoeff()
{
	return m_fBlurCoeff;
}

VOID CParticleSystem::Update( FLOAT fTime )
{
	PPARTICLE				pParticle;
	PPARTICLE*				ppParticle;
	FLOAT					t;
	CEmiterListNode*		pEmiters = m_pRoot;

	he3d_CVector			vG;
	he3d_CVector			vPos;

	while( pEmiters )	
	{	
		pEmiters->m_pEmiter->Emit( fTime );	
		pEmiters = pEmiters->m_pNext;
	}

	ppParticle = &m_pParticle;

	while( *ppParticle )
	{
		pParticle = *ppParticle;

		t = fTime - pParticle->fBornTime;
		pParticle->fFade = 1.0f - t/pParticle->fLifeTime;

		if( pParticle->fFade <= 0.0f )				
		{
			*ppParticle = pParticle->next;
			pParticle->next = m_pParticleFree;
			m_pParticleFree = pParticle;
			m_dwParticleCount--;
		}
		else
		{	
			switch( m_dwGravityType )
			{
				case G_POINT:					
					vG = m_fGravity*Normalize( m_vGravity - pParticle->vP );
					break;

				case G_VECTOR:
					vG = m_fGravity*m_vGravity;
					break;
			}			

			if( pParticle->dwType&PT_SPARK )
				vG = 0.5*vG;			

			if( m_dwGravityType == G_POINT && Length( pParticle->vP - m_vGravity ) < 5.0f )
			{
				*ppParticle = pParticle->next;
				pParticle->next = m_pParticleFree;
				m_pParticleFree = pParticle;
				m_dwParticleCount--;
				continue;
			}

			pParticle->vP  = pParticle->vP0 + pParticle->vV0*t + 0.5*vG*t*t;
			pParticle->vV  = pParticle->vV0 + vG*t;			

			if( m_dwCollision == C_PLANE && DotProd( pParticle->vP, m_vN ) <  -m_fD )
			{
				
				*ppParticle = pParticle->next;
				pParticle->next = m_pParticleFree;
				m_pParticleFree = pParticle;
				m_dwParticleCount--;
					
				if( pParticle->dwType&PT_EMITSSPARKS )
				{											 
					FLOAT					t;
					FLOAT					coeff = DotProd( m_vN, pParticle->vP ) + m_fD;	
					
					t = -coeff/DotProd( m_vN, pParticle->vV );
					vPos = t*pParticle->vV + pParticle->vP;

					// sparks emition
					DWORD					dwEmit = m_dwSparksEmitCount;
					PPARTICLE				pSpark;
					he3d_CVector			vVel;

					if( dwEmit > m_dwMaxParticles - m_dwParticleCount )
						dwEmit = m_dwMaxParticles - m_dwParticleCount;

					m_dwParticleCount += dwEmit;

					for( DWORD i = 0 ; i < dwEmit ; i++ )
					{
						if( m_pParticleFree )
						{		
							pSpark = m_pParticleFree;
							m_pParticleFree = m_pParticleFree->next;
						}
						else
						{
							pSpark = new PARTICLE;
						}

						pSpark->next = m_pParticle;
						m_pParticle = pSpark;						
						
						vVel = -(m_fSparksFalloff + 0.2f*m_fSparksFalloff*SIGNEDRAND())*pParticle->vV;
						
						pSpark->vV0 = vVel*RotationMtx( m_fSparksEmitAngle*SIGNEDRAND(), m_fSparksEmitAngle*SIGNEDRAND(), m_fSparksEmitAngle*SIGNEDRAND() );
						pSpark->vP0 = vPos;
						pSpark->vP = vPos;
						pSpark->fBornTime = fTime;
						pSpark->fLifeTime = m_fSparksLifeTime;
						pSpark->fSize = m_fSparksSize + 0.5f*m_fSparksSize*SIGNEDRAND();
						pSpark->dwType = PT_SPARK;
					}
				}								
			}			
			else			
			{			
				ppParticle = &(pParticle->next);		
			}
		}
	}	
}

VOID CParticleSystem::SetRenderType( DWORD dwType )
{
	m_dwRenderType = dwType;
}

DWORD CParticleSystem::GetRenderType()
{
	return m_dwRenderType;
}

CParticleSystem::Render( PDIRECT3DDEVICE8 pDevice )
{
	pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, TRUE );
    pDevice->SetRenderState( D3DRS_POINTSCALEENABLE,  TRUE );    
    pDevice->SetRenderState( D3DRS_POINTSIZE_MIN, FLT2DWORD(0.10f) );
    pDevice->SetRenderState( D3DRS_POINTSCALE_A,  FLT2DWORD(1.00f) );
    pDevice->SetRenderState( D3DRS_POINTSCALE_B,  FLT2DWORD(0.00f) );
    pDevice->SetRenderState( D3DRS_POINTSCALE_C,  FLT2DWORD(2.00f) );
	pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );

	if( m_dwRenderType == RT_ADD )
	{	
		pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
		pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );		
		pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2 );
		pDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
	}
	else 
	{
		pDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
		pDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );		
		pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
		pDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );
	}

	DWORD					dwZWEnable;
	
	pDevice->GetRenderState( D3DRS_ZWRITEENABLE, &dwZWEnable );
	pDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );

	pDevice->SetTexture( 0, m_ptextTexture );		
	
    pDevice->SetStreamSource( 0, m_pvbParticle, sizeof(PARTICLEVERTEX) );
    pDevice->SetVertexShader( FVF_PARTICLEVERTEX );
	
	PPARTICLE				pParticle = m_pParticle;
	PPARTICLEVERTEX			pVertices;
	DWORD					dwRenderCount = 0;
	DWORD					dwOffset;
	
	he3d_CVector			vel;
	he3d_CVector			pos;
	FLOAT					coeff;	
	FLOAT					size;
	FLOAT					base;

	DWORD					dwColor;

	DWORD					dwSteps;

	dwOffset = m_dwCurrentOffset;
	m_dwCurrentOffset += m_dwLockSize;
	
	if( m_dwCurrentOffset < m_dwBufferSize )
	{
		m_pvbParticle->Lock( dwOffset*sizeof(PARTICLEVERTEX), m_dwLockSize*sizeof(PARTICLEVERTEX), (LPBYTE*)&pVertices, D3DLOCK_NOOVERWRITE );
	}
	else
	{
		m_dwCurrentOffset = 0;
		dwOffset = 0;
		m_pvbParticle->Lock( 0, m_dwLockSize*sizeof(PARTICLEVERTEX), (LPBYTE*)&pVertices, D3DLOCK_DISCARD );
	}

	while( pParticle )
	{
		vel		= pParticle->vV;
		pos		= pParticle->vP;
		size	= pParticle->fSize;
		dwSteps	= 1;

		if( pParticle->dwType&PT_BLURED )
		{		
			coeff	= Length( vel );
			base	= Length( pParticle->vV0 );

			if( coeff < base*0.125f )       dwSteps = 2;
			else if( coeff <  base*0.250f ) dwSteps = 3;
			else if( coeff <  base*0.375f ) dwSteps = 4;
			else if( coeff <  base*0.500f ) dwSteps = 5;
			else if( coeff <  base*0.625f ) dwSteps = 6;
			else if( coeff <  base*0.750f ) dwSteps = 7;
			else		  				    dwSteps = 8;

			size	= size/(FLOAT)dwSteps;
			coeff	= (-m_fBlurCoeff*(sqrtf(pParticle->fSize+1.0f)-1.0f)/(FLOAT)dwSteps);
			vel		= coeff*vel;
		}
		
		dwColor	= (DWORD)(pParticle->fFade*255.0f);
		dwColor = ( dwColor<<24 ) | 0x0;
		
		for( DWORD i = 0 ; i < dwSteps ; i++ )
		{
			pVertices->color = dwColor;
			pVertices->p	 = pos;
			pVertices->size	 = size*((FLOAT)dwSteps - (FLOAT)i*0.5f);
			pVertices++;

			if( ++dwRenderCount == m_dwLockSize )
			{
				m_pvbParticle->Unlock();
				pDevice->DrawPrimitive( D3DPT_POINTLIST, dwOffset, m_dwLockSize );

				dwOffset = m_dwCurrentOffset;
				m_dwCurrentOffset += m_dwLockSize;
				
				if( m_dwCurrentOffset < m_dwBufferSize )
				{
					m_pvbParticle->Lock( dwOffset*sizeof(PARTICLEVERTEX), m_dwLockSize*sizeof(PARTICLEVERTEX), (LPBYTE*)&pVertices, D3DLOCK_NOOVERWRITE );
				}
				else
				{
					m_dwCurrentOffset = 0;
					dwOffset = 0;
					m_pvbParticle->Lock( 0, m_dwLockSize*sizeof(PARTICLEVERTEX), (LPBYTE*)&pVertices, D3DLOCK_DISCARD );
				}

				dwRenderCount = 0;
			}		

			pos = pos + vel;
		}

		pParticle = pParticle->next;
	}

	m_pvbParticle->Unlock();

	if( dwRenderCount )
	{			
		pDevice->DrawPrimitive( D3DPT_POINTLIST, dwOffset, dwRenderCount );
	}

	
	pDevice->SetRenderState( D3DRS_POINTSPRITEENABLE, FALSE );
    pDevice->SetRenderState( D3DRS_POINTSCALEENABLE,  FALSE );        
	pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );	

	pDevice->SetTexture( 0, NULL );

	pDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
	pDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE );

	pDevice->SetRenderState( D3DRS_ZWRITEENABLE, dwZWEnable );	    

	return TRUE;
}

																														   